package com.net.xpay.common.service.impl;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.net.xpay.common.domain.User;
import com.net.xpay.common.domain.partner.*;
import com.net.xpay.common.enums.partner.WithdrawStatus;
import com.net.xpay.common.helper.PartnerAccountDetailHelper;
import com.net.xpay.core.constant.CoreConstant;
import com.net.xpay.core.constant.PoseidonErrorCode;
import com.net.xpay.core.domain.PayTrans;
import com.net.xpay.core.domain.PayTransBatch;
import com.net.xpay.core.domain.safePay.SafePayBatch;
import com.net.xpay.core.domain.safePay.SafePayOrder;
import com.net.xpay.core.enums.*;
import com.net.xpay.core.manager.PayTransBatchManager;
import com.net.xpay.core.manager.PayTransManager;
import com.net.xpay.core.manager.safePay.SafePayOrderManager;
import com.net.xpay.core.model.SafePayBatchCreateModel;
import com.net.xpay.core.model.SafePayTransModel;
import com.net.xpay.core.service.safePay.SafePayService;
import com.net.common.exception.BusinessException;
import com.net.common.util.MoneyUtil;
import com.net.common.util.SerializationUtil;
import com.net.common.util.StreamUtil;
import com.net.xpay.common.domain.partner.*;
import com.net.xpay.common.manager.partner.PartnerAccountManager;
import com.net.xpay.common.manager.partner.PartnerManager;
import com.net.xpay.common.manager.partner.PartnerWithdrawRecordBatchManager;
import com.net.xpay.common.manager.partner.PartnerWithdrawRecordManager;
import com.net.xpay.common.service.PartnerAccountService;
import com.net.xpay.common.service.PartnerWithdrawService;
import com.net.xpay.core.enums.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 2020/9/4 19:13

 */
@Slf4j
@Service
public class PartnerWithdrawServiceImpl implements PartnerWithdrawService {

    @Autowired
    private PartnerAccountManager partnerAccountManager;

    @Autowired
    private PartnerWithdrawRecordManager partnerWithdrawRecordManager;

    @Autowired
    private PartnerWithdrawRecordBatchManager partnerWithdrawRecordBatchManager;

    @Autowired
    private PartnerAccountDetailHelper partnerAccountDetailHelper;

    @Autowired
    private PartnerAccountService partnerAccountService;

    @Autowired
    private PartnerManager partnerManager;

    @Autowired
    private PayTransBatchManager payTransBatchManager;

    @Autowired
    private PayTransManager payTransManager;

    @Autowired
    private SafePayService safePayService;

    @Autowired
    private PartnerWithdrawService partnerWithdrawService;

    @Autowired
    private SafePayOrderManager safePayOrderManager;

    /**
     * 生成合伙人提现记录和批次
     */
    @Override
    @Transactional(transactionManager = "xpayCommonTransactionManager", rollbackFor = Exception.class)
    public void createPartnerWithdrawBatch() {
        log.info("start createPartnerWithdraw");
        List<PartnerAccount> partnerAccounts = partnerAccountManager.listHasLeftMoneyAccount();

        if (CollectionUtils.isEmpty(partnerAccounts)) {
            log.info("no account has left money");
            return;
        }

        log.info("createPartnerWithdraw partnerAccounts size={}|accounts={}", partnerAccounts.size(), SerializationUtil.obj2String(partnerAccounts));

        long totalMoney = 0L;
        List<Long> withdrawRecordIds = Lists.newArrayList();
        for (PartnerAccount partnerAccount : partnerAccounts) {
            Partner partner = partnerManager.getById(partnerAccount.getId());
            if (Boolean.FALSE.equals(partner.getAutoWithdraw())) {
                continue;
            }
            if (partnerAccount.getMoneyLeft() < CoreConstant.PARTNER_WITHDRAW_MIN_MONEY) {
                continue;
            }
            //生成提现记录
            PartnerWithdrawRecord withdrawRecord = createPartnerWithdrawRecord(partnerAccount.getId(), partnerAccount.getMoneyLeft(), partner.getAlipayName(), partner.getAlipayAccount());
            if (withdrawRecord == null) {
                continue;
            }
            totalMoney += withdrawRecord.getTransferMoney();
            withdrawRecordIds.add(withdrawRecord.getId());
        }
        if (CollectionUtils.isEmpty(withdrawRecordIds)) {
            return;
        }

        //生成提现批次
        PartnerWithdrawRecordBatch recordBatch = PartnerWithdrawRecordBatch.getInstance();
        recordBatch.setStatus(WithdrawStatus.WAIT_PAY);
        recordBatch.setIsOffline(0);
        recordBatch.setTotalNum(withdrawRecordIds.size());
        recordBatch.setTotalMoney(totalMoney);
        recordBatch.setWithdrawRecordIds(withdrawRecordIds);

        if (recordBatch.getTotalMoney() == 0L) {
            recordBatch.setStatus(WithdrawStatus.PAYED);
            recordBatch.setDealingTime(LocalDateTime.now());
            recordBatch.setEndTime(LocalDateTime.now());
        }
        partnerWithdrawRecordBatchManager.save(recordBatch);

        log.info("end createPartnerWithdraw");
    }

    @Override
    @Transactional(transactionManager = "xpayCommonTransactionManager", rollbackFor = Exception.class)
    public PartnerWithdrawRecord createPartnerWithdrawRecord(Long partnerId, Long transferMoney, String alipayName, String alipayAccount) {
        //加锁重新查出来
        PartnerAccount partnerAccount = partnerAccountManager.getByIdWithLock(partnerId);
        if (transferMoney < CoreConstant.PARTNER_WITHDRAW_MIN_MONEY) {
            return null;
        }
        Partner partner = partnerManager.getById(partnerId);
        //提现
        PartnerWithdrawRecord withdrawRecord = PartnerWithdrawRecord.getInstance(partner.getId(), transferMoney);
        withdrawRecord.setAlipayAccount(alipayAccount);
        withdrawRecord.setAlipayName(alipayName);
        partnerWithdrawRecordManager.save(withdrawRecord);
        //扣钱
        PartnerAccountDetail accountDetail = PartnerAccountDetail.getInstance(partner.getId(), withdrawRecord.getId());
        partnerAccountDetailHelper.initCommonAccountDetail(accountDetail, AccountBillType.PARTNER_WITHDRAW, -withdrawRecord.getTransferMoney(), "提现");
        partnerAccountService.onPay(partner, partnerAccount, accountDetail);

        return withdrawRecord;
    }

    @Override
    @Transactional(transactionManager = "xpayCommonTransactionManager", rollbackFor = Exception.class)
    public void transPartnerWithdrawBatch(Long id, User transUser, String alipayAccount) {
        //重新查出来
        PartnerWithdrawRecordBatch batch = partnerWithdrawRecordBatchManager.getById(id);

        if (!WithdrawStatus.WAIT_PAY.equals(batch.getStatus())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "合伙人提现批次不是待支付状态");
        }
        if (batch.getTotalMoney() == 0L) {
            batch.setStatus(WithdrawStatus.PAYED);
            batch.setDealingTime(LocalDateTime.now());
            batch.setEndTime(LocalDateTime.now());
            partnerWithdrawRecordBatchManager.updateByPrimaryKeyWithVersion(batch);
            return;
        }

        //转账
        SafePayBatchCreateModel batchModel = new SafePayBatchCreateModel();
        batchModel.setAlipayNickName(alipayAccount);
        batchModel.setType(PayeeType.ALIPAY);
        batchModel.setTotalTransAmount(MoneyUtil.convertCentToString(batch.getTotalMoney(), 2));
        batchModel.setNeedNotify(false);
        batchModel.setTotalCount((long) batch.getTotalNum());
        batchModel.setImportBatchNo(batch.getBizId());

        //提现明细
        List<PartnerWithdrawRecord> records = partnerWithdrawRecordManager.listByIds(batch.getWithdrawRecordIds());
        List<SafePayTransModel> list = new ArrayList<>();
        for (PartnerWithdrawRecord record : records) {
            SafePayTransModel safePayTransModel = new SafePayTransModel();
            safePayTransModel.setImportBizNo(record.getId().toString());
            safePayTransModel.setName(record.getAlipayName().trim());
            safePayTransModel.setIdentity(record.getAlipayAccount().trim().replace(" ", ""));
            safePayTransModel.setTransAmount(MoneyUtil.convertCentToString(record.getTransferMoney(), 2));
            safePayTransModel.setRemark(record.getRemark());
            list.add(safePayTransModel);
        }

        batchModel.setPayTrans(list);
        batchModel.setConfirmTrans(1);
        safePayService.createSafePayBatch(transUser, batchModel);

        batch.setStatus(WithdrawStatus.DEALING);
        batch.setDealingTime(LocalDateTime.now());
        partnerWithdrawRecordBatchManager.updateByPrimaryKeyWithVersion(batch);
    }

    @Override
    @Transactional(transactionManager = "xpayCommonTransactionManager", rollbackFor = Exception.class)
    public void updatePartnerWithdrawBatchResultBySafePay(User user, SafePayBatch safePayBatch) {
        long startTime = System.currentTimeMillis();
        PartnerWithdrawRecordBatch batch = partnerWithdrawRecordBatchManager.findByBizId(safePayBatch.getImportBatchNo());

        if (batch == null) {
            log.error("updatePartnerWithdrawBatchResultBySafePay error importBatchNo={}", safePayBatch.getImportBatchNo());
            return;
        }

        if (!SafePayBatchStatus.isFinalStatus(safePayBatch.getStatus())) {
            return;
        }

        if (!WithdrawStatus.DEALING.equals(batch.getStatus())) {
            return;
        }

        List<SafePayOrder> payTrans = safePayOrderManager.listByBatchId(user.getId(), safePayBatch.getId());

        processWithdrawRecordWithSafePayOrder(payTrans, batch);

        batch.setBatchTransId(batch.getBatchTransId());
        batch.setStatus(WithdrawStatus.PAYED);
        batch.setEndTime(LocalDateTime.now());
        partnerWithdrawRecordBatchManager.updateByPrimaryKeyWithVersion(batch);

        log.info("updatePartnerWithdrawBatchResultBySafePay success bizId={}|cost={}", batch.getBizId(), System.currentTimeMillis() - startTime);
    }

    /**
     * 跟新打款结果
     *
     * @param batch     提现批次
     * @param transUser 打款用户
     */
    @Override
    @Transactional(transactionManager = "xpayCommonTransactionManager", rollbackFor = Exception.class)
    public void updatePartnerWithdrawBatchResult(PartnerWithdrawRecordBatch batch, User transUser) {
        long startTime = System.currentTimeMillis();
        //重新查出来
        batch = partnerWithdrawRecordBatchManager.getById(batch.getId());

        PayTransBatch payTransBatch = payTransBatchManager.findByImportBatchNo(transUser.getId(), batch.getBizId());

        if (payTransBatch == null) {
            log.error("updatePartnerWithdrawBatchResult error importBatchNo={}", batch.getBizId());
            return;
        }
        if (!PayTransBatchStatus.isFinalStatus(payTransBatch.getStatus())) {
            return;
        }

        List<PayTrans> payTrans = payTransManager.listByPayTransBatchId(transUser.getId(), payTransBatch.getId());

        processWithdrawRecord(payTrans, batch);

        batch.setBatchTransId(payTransBatch.getBatchTransId());
        batch.setStatus(WithdrawStatus.PAYED);
        batch.setEndTime(LocalDateTime.now());
        partnerWithdrawRecordBatchManager.updateByPrimaryKeyWithVersion(batch);

        log.info("updatePartnerWithdrawBatchResult success bizId={}|cost={}", batch.getBizId(), System.currentTimeMillis() - startTime);
    }

    @Override
    @Transactional(transactionManager = "xpayCommonTransactionManager", rollbackFor = Exception.class)
    public void cancelWithdrawRecord(Long id, String remark) {
        Preconditions.checkArgument(id != null);
        PartnerWithdrawRecord record = partnerWithdrawRecordManager.getById(id);
        Partner partner = partnerManager.getById(record.getPartnerId());
        PartnerAccount partnerAccount = partnerAccountManager.getByIdWithLock(partner.getId());
        if (!WithdrawStatus.WAIT_PAY.equals(record.getStatus())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "当前状态无法取消");
        }

        record.setStatus(WithdrawStatus.FAILED);
        record.setEndTime(LocalDateTime.now());
        record.setRemark(remark);
        partnerWithdrawRecordManager.updateByPrimaryKeyWithVersion(record);

        //退钱
        PartnerAccountDetail accountDetail = PartnerAccountDetail.getInstance(record.getPartnerId(), id);
        partnerAccountDetailHelper.initCommonAccountDetail(accountDetail, AccountBillType.PARTNER_WITHDRAW_REFUND, record.getTransferMoney(), "提现退回");
        partnerAccountService.onPay(partner, partnerAccount, accountDetail);
    }

    @Override
    @Transactional(transactionManager = "xpayCommonTransactionManager", rollbackFor = Exception.class)
    public void ensurePartnerWithdrawBatch(Long id) {
        PartnerWithdrawRecordBatch batch = partnerWithdrawRecordBatchManager.getById(id);
        if (WithdrawStatus.PAYED.equals(batch.getStatus())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "转账已完成，请勿重复转账");
        }
        batch.setIsOffline(1);
        batch.setStatus(WithdrawStatus.PAYED);
        batch.setEndTime(LocalDateTime.now());
        batch.setDealingTime(LocalDateTime.now());
        batch.setRemark("线下转账");
        partnerWithdrawRecordBatchManager.updateByPrimaryKeyWithVersion(batch);

        List<Long> withdrawRecordIds = batch.getWithdrawRecordIds();
        if (CollectionUtils.isEmpty(withdrawRecordIds)) {
            return;
        }
        List<PartnerWithdrawRecord> withdrawRecords = partnerWithdrawRecordManager.listByIds(withdrawRecordIds);

        for (PartnerWithdrawRecord withdrawRecord : withdrawRecords) {
            withdrawRecord.setIsOffline(1);
            withdrawRecord.setStatus(WithdrawStatus.PAYED);
            withdrawRecord.setEndTime(LocalDateTime.now());
            withdrawRecord.setRemark("线下转账");
            partnerWithdrawRecordManager.updateByPrimaryKeyWithVersion(withdrawRecord);
        }
    }

    private void processWithdrawRecord(List<PayTrans> payTransList, PartnerWithdrawRecordBatch partnerWithdrawRecordBatch) {
        List<Long> ids = partnerWithdrawRecordBatch.getWithdrawRecordIds();
        List<PartnerWithdrawRecord> records = partnerWithdrawRecordManager.listByIds(ids);
        Map<Long, PartnerWithdrawRecord> recordMap = StreamUtil.toMap(records, PartnerWithdrawRecord::getId);
        for (PayTrans model : payTransList) {
            PartnerWithdrawRecord record = recordMap.get(Long.valueOf(model.getImportBizNo()));
            if (record == null) {
                continue;
            }
            if (PayTransStatus.SUCCESS == model.getStatus()) {
                if (record.getEndTime() == null) {
                    record.setStatus(WithdrawStatus.PAYED);
                    record.setEndTime(LocalDateTime.now());
                    partnerWithdrawRecordManager.updateByPrimaryKeyWithVersion(record);
                }
            } else {
                record.setRemark(model.getErrorMsg());
                partnerWithdrawRecordManager.updateByPrimaryKeyWithVersion(record);
            }
        }
    }

    private void processWithdrawRecordWithSafePayOrder(List<SafePayOrder> payTransList, PartnerWithdrawRecordBatch partnerWithdrawRecordBatch) {
        List<Long> ids = partnerWithdrawRecordBatch.getWithdrawRecordIds();
        List<PartnerWithdrawRecord> records = partnerWithdrawRecordManager.listByIds(ids);
        Map<Long, PartnerWithdrawRecord> recordMap = StreamUtil.toMap(records, PartnerWithdrawRecord::getId);
        for (SafePayOrder model : payTransList) {
            PartnerWithdrawRecord record = recordMap.get(Long.valueOf(model.getImportBizNo()));
            if (record == null) {
                continue;
            }
            if (SafePayOrderStatus.SUCCESS == model.getStatus()) {
                if (record.getEndTime() == null) {
                    record.setStatus(WithdrawStatus.PAYED);
                    record.setEndTime(LocalDateTime.now());
                    partnerWithdrawRecordManager.updateByPrimaryKeyWithVersion(record);
                }
            } else {
                partnerWithdrawService.cancelWithdrawRecord(record.getId(), model.getErrorMsg());
            }
        }
    }
}
